package com.testlio.assignment.service;

import com.testlio.assignment.dto.IssueComparisonDTO;
import com.testlio.assignment.dto.IssueRevisionDTO;
import com.testlio.assignment.model.Issue;
import com.testlio.assignment.model.IssueRevision;
import com.testlio.assignment.repository.IssueRevisionRepository;
import com.testlio.assignment.repository.IssueRepository;
import com.testlio.assignment.mapper.IssueRevisionMapper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.HashMap;
import java.util.Objects;
import com.testlio.assignment.dto.IssueComparisonDTO.ChangedValue;

@Service
@Transactional
@Slf4j
@AllArgsConstructor
public class IssueRevisionService {
    private final IssueRevisionRepository revisionRepository;
    private final IssueRepository issueRepository;
    private final IssueRevisionMapper issueRevisionMapper;

    public void createRevision(Issue issue, Map<String, String> changes, String updatedBy) {
        IssueRevision revision = new IssueRevision();
        revision.setIssueId(issue.getId());
        revision.setTitle(changes.getOrDefault("title", issue.getTitle()));
        revision.setDescription(changes.getOrDefault("description", issue.getDescription()));
        revision.setUpdatedBy(updatedBy);
        revision.setUpdatedAt(LocalDateTime.now());
        revisionRepository.save(revision);
    }

    public List<IssueRevisionDTO> getRevisions(Integer issueId) {
        return revisionRepository.findByIssueIdOrderByUpdatedAtDesc(issueId)
                .stream()
                .map(this::toDTO)
                .collect(Collectors.toList());
    }

    private IssueRevisionDTO toDTO(IssueRevision revision) {
        IssueRevisionDTO dto = new IssueRevisionDTO();
		Issue issueItem = issueRepository.getReferenceById(revision.getIssueId());

        Map<String, String> issue = new HashMap<>();
        issue.put("title", issueItem.getTitle());
        issue.put("description", issueItem.getDescription());
        dto.setIssue(issue);

        Map<String, String> changes = new HashMap<>();
        if (revision.getTitle() != null) {
            changes.put("title", revision.getTitle());
        }
        if (revision.getDescription() != null) {
            changes.put("description", revision.getDescription());
        }

        dto.setChanges(changes);
        dto.setUpdatedAt(revision.getUpdatedAt());
        return dto;
    }

    public IssueComparisonDTO compareRevisions(Integer issueId, Integer revisionA, Integer revisionB) {
        // 确保issue存在
        Issue issue = issueRepository.findById(issueId)
                .orElseThrow(() -> new EmptyResultDataAccessException(1));

        // 获取所有revisions并按ID排序
        List<IssueRevision> allRevisions = revisionRepository.findByIssueIdOrderByIdAsc(issueId);

        // 确定新旧revision
	    Integer olderRevisionId = Math.min(revisionA, revisionB);
	    Integer newerRevisionId = Math.max(revisionA, revisionB);

        // 获取两个时间点之间的所有revisions
        List<IssueRevision> revisionsBetween = allRevisions.stream()
                .filter(r -> r.getId() >= olderRevisionId && r.getId() <= newerRevisionId)
                .collect(Collectors.toList());

        if (revisionsBetween.isEmpty()) {
            throw new EmptyResultDataAccessException(1);
        }

        // 计算older revision时的完整issue状态
        Map<String, Object> beforeState = calculateIssueStateAtRevision(issue, allRevisions, olderRevisionId);

        // 计算newer revision时的完整issue状态
        Map<String, Object> afterState = calculateIssueStateAtRevision(issue, allRevisions, newerRevisionId);

        // 计算变更
        Map<String, ChangedValue> changes = calculateChanges(beforeState, afterState);

        return IssueComparisonDTO.builder()
                .before(beforeState)
                .after(afterState)
                .changes(changes)
                .revisions(revisionsBetween.stream()
                        .map(issueRevisionMapper::toDTO)
                        .collect(Collectors.toList()))
                .build();
    }

    private Map<String, Object> calculateIssueStateAtRevision(Issue issue, List<IssueRevision> allRevisions, Integer targetRevisionId) {
        // 获取目标revision之前（包括目标revision）的所有revisions
        List<IssueRevision> revisionsUpTo = allRevisions.stream()
                .filter(r -> r.getId() <= targetRevisionId)
                .collect(Collectors.toList());

        // 创建issue的可变副本
        Map<String, Object> currentState = new HashMap<>();
        currentState.put("title", issue.getTitle());
        currentState.put("description", issue.getDescription());

        // 按时间顺序应用每个revision的变更
        for (IssueRevision revision : revisionsUpTo) {
            applyRevision(currentState, revision);
        }

        return currentState;
    }

    private void applyRevision(Map<String, Object> state, IssueRevision revision) {
        // 只更新revision中非空的字段
        if (revision.getTitle() != null) {
            state.put("title", revision.getTitle());
        }
        if (revision.getDescription() != null) {
            state.put("description", revision.getDescription());
        }
    }

    private Map<String, ChangedValue> calculateChanges(Map<String, Object> before, Map<String, Object> after) {
        Map<String, ChangedValue> changes = new HashMap<>();

        if (!Objects.equals(before.get("title"), after.get("title"))) {
            changes.put("title", new ChangedValue(before.get("title"), after.get("title")));
        }

        if (!Objects.equals(before.get("description"), after.get("description"))) {
            changes.put("description", new ChangedValue(before.get("description"), after.get("description")));
        }

        return changes;
    }
}
